#ifndef __H_ROOM__
#define __H_ROOM__
#include <iostream>
#include <vector>
#include "util.hpp"
#include <unordered_set>
#include <string>
#include "db.hpp"
#include "online.hpp"
typedef enum{ROOM_START,ROOM_OVER}room_status;
enum Color 
{
    CHESS_BLACK=1,
    CHESS_WHITE
};
class room
{
    #define BOARD_ROW 15
    #define BOARD_COL 15
    private:
        int same_count(int row,int col,int row_off,int col_off,Color color)
        {
            //判断一个方向上的同色棋子数量
            //方向分为：上、左上、左、左下、下、右下、右、右上
            int ret = 0;
            for(int i = row,j=col ; i >= 0 && i < _board.size() && j >= 0 && j < _board[0].size() ; i += row_off , j += col_off)
            {
                if(_board[i][j] == color) ++ret;
                else break;
            }
            return ret;
        }
        uint64_t check_winner(Color cur,int row,int col)
        {
            uint64_t cur_id = cur == CHESS_BLACK ? _black_player_id : _white_player_id;
            int five = 0;
            //判断横行
            five = same_count(row,col,0,-1,cur);
            five += same_count(row,col,0,1,cur)-1;
            if(five == 5) return cur_id;
            //判断竖列
            five = same_count(row,col,-1,0,cur);
            five += same_count(row,col,1,0,cur)-1;
            if(five == 5) return cur_id;
            //判断对角线"/"
            five = same_count(row,col,1,-1,cur);
            five += same_count(row,col,-1,1,cur)-1;
            if(five == 5) return cur_id;
            //判断对角线"\"
            five = same_count(row,col,1,1,cur);
            five += same_count(row,col,-1,-1,cur)-1;
            if(five == 5) return cur_id;
            return 0;
        }
    public:
        room(uint64_t room_id,user_table* tb_user,online_user_manage* online):
        _room_id(room_id),_tb_user(tb_user),_online(online),_status(ROOM_START),_player_num(0),_board(BOARD_ROW,std::vector<char>(BOARD_COL,0))
        {
            add_sensitive_str("傻逼");
            add_sensitive_str("垃圾");
            add_sensitive_str("弱智");
            DLOG("房间创建成功！！");
        }
        ~room()
        {
            DLOG("房间销毁成功！！");
        }
        void add_sensitive_str(std::string str) {_sensitive.insert(str);}
        uint64_t id(){return _room_id;}
        int PlayerCount(){return _player_num;}
        room_status status(){return _status;}
        void add_white_user(uint64_t uid){_white_player_id = uid;_player_num++;}
        void add_black_user(uint64_t uid){_black_player_id = uid;_player_num++;}
        uint64_t get_white_id(){return _white_player_id;}
        uint64_t get_black_id(){return _black_player_id;}

        //处理下棋动作
        Json::Value handle_chess(Json::Value& req)
        {
            Json::Value json_resp = req; 
            // 判断房间中两个玩家是否都在线，任意一个不在线，就是另一方胜利
            uint64_t cur_id = req["uid"].asUInt64(); //cur_id表示发送下棋请求的用户ID
            uint64_t other_id = cur_id == _white_player_id ? _black_player_id : _white_player_id; //other_id表示另一方的用户ID
            Color cur_color = cur_id == _white_player_id ? CHESS_WHITE : CHESS_BLACK;
            Color other_color = cur_id == _white_player_id ? CHESS_BLACK : CHESS_WHITE;
            if(!_online->is_in_game_room(other_id))
            {
                json_resp["result"] = true;
                json_resp["reason"] = "运气真好，对方掉线，不战而胜！";
                json_resp["winner"] = (unsigned long long)cur_id;
                _status = ROOM_OVER;
                return json_resp;
            }

            // 3、获取走棋位置，判断当前走棋是否合理（位置是否已经被占用）
            int chess_row = req["row"].asInt() , chess_col = req["col"].asInt();
            if(_board[chess_row][chess_col] != 0)
            {
                json_resp["result"] = false;
                json_resp["reason"] = "该位置已被占用!";
                return json_resp;
            }
            //下棋位置合理
            _board[chess_row][chess_col] = cur_color;
            // 4、判断是否有玩家胜利（从当前走棋位置开始判断是否存在五星连珠）
            bool ret = check_winner(cur_color,chess_row,chess_col);
            if(ret)
            {
                ELOG("有玩家胜利");
                json_resp["winner"] = (unsigned long long)cur_id;
                json_resp["reason"] = "五星连珠，你赢了！";
                _status = ROOM_OVER;
            }
            else
            {   
                json_resp["winner"] = 0;
            }
            DLOG("winner is : %d",json_resp["winner"].asUInt64());
            json_resp["result"] = true; 
            std::string body;
            json_util::Serialize(json_resp,body);
            std::cout << body << std::endl;
            return json_resp;
        }
        //处理聊天动作
        Json::Value handle_chat(Json::Value& req)
        {
            Json::Value json_resp = req;
            //检测消息中是否包含敏感词
            std::string words = req["message"].asString();
            for(auto& iter : _sensitive)
            {
                if(words.find(iter,0) != std::string::npos)
                {
                    json_resp["result"] = false;
                    json_resp["reason"] = "消息中包含敏感词，不能发送！";
                    return json_resp;
                }
            }
            //广播消息-返回消息
            json_resp["result"] = true;

            boardcast(json_resp);
            return json_resp;
        }
        //处理玩家退出房间动作
        void handle_exit(uint64_t uid)
        {
            //如果是下棋中退出，则对方胜利。下棋结束退出，正常退出！
            Json::Value json_resp;
            if(_status == ROOM_START)
            {
                uint64_t winner_id = (unsigned long long)(uid == _white_player_id ? _black_player_id : _white_player_id);
                uint64_t loser_id = (unsigned long long)(winner_id == _white_player_id ? _black_player_id : _white_player_id);
                json_resp["optype"] = "put_chess";
                json_resp["room_id"] = (unsigned long long)_room_id;
                json_resp["uid"] = (unsigned long long)uid;
                json_resp["row"] = -1;
                json_resp["col"] = -1;
                json_resp["result"] = true;
                json_resp["reason"] = "运气真好，对方掉线，不战而胜！";
                json_resp["winner"] = (unsigned long long)winner_id;
                _status = ROOM_OVER;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                boardcast(json_resp);
            }
            --_player_num;
        }
        //总的请求处理函数，在函数体内部，区分请求类型。根据不同的请求调用不同的处理函数，得到响应进行广播
        void handle_request(Json::Value& req)
        {
            //校验房间号是否匹配
            Json::Value json_resp = req;
            uint64_t room_id = req["room_id"].asUInt64();
            if(room_id != _room_id)
            {
                json_resp["result"] = false;
                json_resp["reason"] = "请求的游戏房间不匹配";
            }
            else 
            {
                if(req["optype"].asString() == "put_chess")
                {
                    json_resp = handle_chess(req);
                    //判断是否更新数据库
                    uint64_t win_id = json_resp["winner"].asUInt64();
                    if(win_id != 0)
                    {
                        _tb_user->win(win_id);
                        _tb_user->lose(win_id == _white_player_id ? _black_player_id : _white_player_id);
                        _status = ROOM_OVER;
                    }
                }
                else if(req["optype"].asString() == "chat")
                {
                    json_resp = handle_chat(req);
                }
                else 
                {
                    json_resp["result"] = false;
                    json_resp["reason"] = "未知请求类型";
                }
            }
            boardcast(json_resp);
        }
        //将指定的信息广播给房间中的所有玩家
        void boardcast(Json::Value& rsp)
        {
            //对要响应的信息进行序列化，将Json::Value中的数据序列化成为json格式字符串
            std::string body;
            json_util::Serialize(rsp,body);
            //获取房间中所有用户的通信连接，并发送响应信息
            wsserver_t::connection_ptr white_conn = _online->get_conn_from_room(_white_player_id);
            wsserver_t::connection_ptr black_conn = _online->get_conn_from_room(_black_player_id);
            if(white_conn != wsserver_t::connection_ptr())
            {
                white_conn->send(body);
            }
            if(black_conn != wsserver_t::connection_ptr())
            {
                black_conn->send(body);
            }
        }
    private:
        uint64_t _room_id; //游戏房间的ID
        room_status _status; //游戏房间的状态
        int _player_num; //游戏房间内的玩家数量 
        uint64_t _white_player_id; //执白棋玩家的ID
        uint64_t _black_player_id; //执黑棋玩家的ID
        user_table* _tb_user; //用户信息表的句柄
        std::vector<std::vector<char>> _board; //棋盘
        online_user_manage* _online; //在线玩家的管理句柄
        std::unordered_set<std::string> _sensitive;
};


using room_ptr = std::shared_ptr<room>;

class room_manager
{
        public:
        //初始化房间计数器
        room_manager(user_table* tb_user,online_user_manage* online_user)
        : _next_rid(1),_tb_user(tb_user),_online_user(online_user)
        {
            DLOG("房间管理模块初始化完毕！");
        }
        ~room_manager(){
            DLOG("房间管理模块即将销毁！");
        }
    public:
        //为两个用户创建房间，并返回房间的智能指针管理对象
        room_ptr create_room(uint64_t uid1,uint64_t uid2)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            //检验两个用户是否都还在游戏大厅中，只有都在才需要创建房间
            if(_online_user->is_in_game_hall(uid1) != true) return room_ptr();
            if(_online_user->is_in_game_hall(uid2) != true) return room_ptr();
            room_ptr ret(new room(_next_rid,_tb_user,_online_user));
            ret->add_black_user(uid1);
            ret->add_white_user(uid2);
            _id_to_room.insert(std::make_pair(_next_rid,ret));
            _user_to_room.insert(std::make_pair(uid1,_next_rid));
            _user_to_room.insert(std::make_pair(uid2,_next_rid));
            ++_next_rid;
            return ret;
        }
        //通过房间ID获取房间信息
        room_ptr get_room_by_rid(uint64_t rid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto iter = _id_to_room.find(rid);
            if(iter == _id_to_room.end())
                return room_ptr();
            return iter->second;
        }
        //通过用户ID获取房间信息 
        room_ptr get_room_by_uid(uint64_t uid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            //先通过用户id找到房间id
            auto iter = _user_to_room.find(uid);
            if(iter == _user_to_room.end())
                return room_ptr();
            //再通过房间id找到房间信息
            uint64_t rid = iter->second;
            auto riter = _id_to_room.find(rid);
            if(riter == _id_to_room.end()) return room_ptr();

            return riter->second;
        }
        //通过房间ID销毁房间
        void remove_room(uint64_t rid)
        {
            //为什么该接口不需要加锁？
            //通过房间ID，获取房间信息
            auto it = _id_to_room.find(rid);
            room_ptr r = it->second;
            //通过房间信息，获取房间中所有用户的ID
            std::unique_lock<std::mutex> lock(_mutex);
            uint64_t uid1 = r->get_black_id();
            uint64_t uid2 = r->get_white_id();
            //移除房间管理中的用户信息
            _user_to_room.erase(uid1);
            _user_to_room.erase(uid2);
            //移除房间管理信息
            _id_to_room.erase(rid);
        }
        //删除房间中指定用户，如果房间中没有用户了，则销毁房间，用户连接断开时被调用
        void remove_room_user(uint64_t uid)
        {
            room_ptr r = get_room_by_uid(uid);
            if(r.get() == nullptr) return;
            r->handle_exit(uid);
            if(r->PlayerCount() == 0)   remove_room(r->id());
        }

    private:
        user_table* _tb_user;
        online_user_manage* _online_user;
        uint64_t _next_rid;
        std::mutex _mutex;
        std::unordered_map<uint64_t, room_ptr> _id_to_room;
        std::unordered_map<uint64_t,uint64_t> _user_to_room;
};
#endif
